use std::process::Stdio;
use std::time::Duration;

use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{Mutex, OnceCell};

use crate::config::get_snmp;

static TRAP: OnceCell<Mutex<Receiver<String>>> = OnceCell::const_new();

async fn get_trap() -> &'static Mutex<Receiver<String>> {
    TRAP.get_or_init(|| async {
        let (tx, rx) = tokio::sync::mpsc::channel(1000);
        tokio::spawn(async move {
            loop {
                blocking_run_snmptrapd(tx.clone()).await;
                tokio::time::sleep(Duration::from_secs(10)).await;
            }
        });
        Mutex::new(rx)
    })
    .await
}

pub async fn blocking_run_snmptrapd(tx: Sender<String>) {
    let snmp_config = get_snmp().await.lock().await.clone();
    let mut child = Command::new("./go-snmptrapd")
        .args([
            "-c",
            &snmp_config.trap_community,
            "-p",
            format!("{}", snmp_config.trap_port).as_str(),
        ])
        .kill_on_drop(true)
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();
    let stdout = child.stdout.take().unwrap();

    let mut reader = BufReader::new(stdout).lines();
    tokio::spawn(async move {
        let status = child
            .wait()
            .await
            .expect("child process encountered an error");
        tracing::info!("child status was: {}", status);
    });

    while let Some(line) = reader.next_line().await.unwrap() {
        if line.contains(&snmp_config.host) {
            tracing::debug!("add snmptrapd line {line}");
            tx.send(line).await.unwrap();
        }
    }
}

pub async fn get_trap_lines() -> Vec<String> {
    let mut trap_lines = vec![];
    let mut rx = get_trap().await.lock().await;
    tokio::time::timeout(
        Duration::from_millis(100),
        get_trap_lines_with_time_limit(&mut rx, &mut trap_lines),
    )
    .await
    .unwrap_or_default();
    trap_lines
}

async fn get_trap_lines_with_time_limit(rx: &mut Receiver<String>, trap_lines: &mut Vec<String>) {
    while let Some(value) = rx.recv().await {
        trap_lines.push(value);
    }
}
